#set( $symbol_pound = '#' )
#set( $symbol_dollar = '$' )
#set( $symbol_escape = '\' )
package ${package}.setup.module.code.controller;

import ${package}.common.bean.po.Code;
import ${package}.common.bean.po.CodeType;
import ${package}.common.bean.vo.AddCodeTypeVo;
import ${package}.common.bean.vo.CodeApiVo;
import ${package}.common.bean.vo.CodeMoveVo;
import ${package}.common.bean.vo.CodeTypeApiVo;
import ${package}.common.bean.vo.CodeTypeVo;
import ${package}.common.bean.vo.CodeVo;
import ${package}.common.bean.vo.InsertCodeVo;
import ${package}.common.bean.vo.JsonResult;
import ${package}.common.bean.vo.QueryBean;
import ${package}.common.bean.vo.UpdateCodeTypeVo;
import ${package}.common.bean.vo.UpdateCodeVo;
import ${package}.common.bean.vo.UploadCodeVo;
import ${package}.common.emum.ReturnCodeEnum;
import ${package}.common.exception.BusinessException;
import ${package}.common.utils.ResourceUtils;
import ${package}.common.utils.ResponseUtil;
import ${package}.common.utils.TreeUtil;
import ${package}.setup.module.code.service.CodeService;
import ${package}.setup.utils.SourceCodeUtil;
import com.github.pagehelper.PageInfo;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.stream.Collectors;

import static ${package}.common.emum.CacheNameConst.CR_CODE;

/**
 * Description: CodeController
 *
 * @author wenxiaopeng
 * @date 2020/07/23 23:15
 *
 * <pre>
 *              ${copyright}
 *      Copyright (c) 2019. All Rights Reserved.
 * </pre>
 */
@Validated
@RestController
@RequestMapping("/code")
@Slf4j
public class CodeController {

	@Resource
	private CodeService codeService;

	@Resource
	private CacheManager cacheManager;

	@PostMapping("/codetype/limits")
	@ApiOperation("分页获取codeType列表")
	public JsonResult<PageInfo<CodeType>> getCodeTypeLimit(@RequestBody @Valid QueryBean queryBean) {
		PageInfo<CodeType> results = codeService.getCodeTypeLimits(queryBean);
		return JsonResult.getInstant(ReturnCodeEnum.SUCCESS, results);
	}

	/**
	 * Description:
	 * <查询代码树>
	 * @param codeType  代码类型
	 * @param code      子代码
	 * @author wenxiaopeng
	 * @date 12:17 2021/06/17
	 * @return ${package}.common.bean.vo.JsonResult<${package}.common.bean.vo.CodeTypeVo>
	 **/
	@GetMapping(value = {"/api/tree/{codeType}/{code}","/api/tree/{codeType}"})
	@ApiOperation("查询代码树")
	@Caching(cacheable = {
			@Cacheable(value = CR_CODE, condition = "${symbol_pound}lazy", key = "'TREE_' + ${symbol_pound}codeType + ${symbol_pound}code + '_LAZY'"),
			@Cacheable(value = CR_CODE, condition = "!${symbol_pound}lazy", key = "'TREE_' + ${symbol_pound}codeType + ${symbol_pound}code")
	})
	public JsonResult<List<CodeApiVo>> getCodesTree(@PathVariable String codeType, @PathVariable(required = false) String code,
													@RequestParam(defaultValue = "false") boolean lazy) {
		log.info("...Get Code Tree: CodeType:{} Code:{} Lazy:{}", codeType, code, lazy);
		List<CodeApiVo> result = codeService.getCodes(codeType, code, lazy);
		Long fatherId = null;
		if(StringUtils.isNotBlank(code)) {
			CodeApiVo codeDetail = codeService.getCodeDetail(code);
			if(null!=codeDetail) {
				fatherId = codeDetail.getId();
			}
		}
		return JsonResult.getInstant(ReturnCodeEnum.SUCCESS, TreeUtil.fillingTree(result, fatherId));
	}

	/**
	 * Description:
	 * <查询代码列表>
	 * @param codeType  代码类型
	 * @param code      子代码
	 * @param lazy      是否懒加载 true时仅返回当前层级
	 * @author wenxiaopeng
	 * @date 12:16 2021/06/17
	 * @return ${package}.common.bean.vo.JsonResult<${package}.common.bean.vo.CodeTypeVo>
	 **/
	@GetMapping(value = {"/api/list/{codeType}/{code}","/api/list/{codeType}"})
	@ApiOperation("查询代码列表")
	@Caching(cacheable = {
			@Cacheable(value = CR_CODE, condition = "${symbol_pound}lazy", key = "'LIST_' + ${symbol_pound}codeType + ${symbol_pound}code + '_LAZY'"),
			@Cacheable(value = CR_CODE, condition = "!${symbol_pound}lazy", key = "'LIST_' + ${symbol_pound}codeType + ${symbol_pound}code")
	})
	public JsonResult<List<CodeApiVo>> getCodesList(@PathVariable String codeType, @PathVariable(required = false) String code,
	                                 @RequestParam(defaultValue = "false") boolean lazy) {
		log.info("...Get Code List: CodeType:{} Code:{} Lazy:{}", codeType, code, lazy);
		return JsonResult.getInstant(ReturnCodeEnum.SUCCESS, codeService.getCodes(codeType, code, lazy));
	}

	/**
	 * Description:
	 * <查询代码含义>
	 * @param code 1
	 * @author wenxiaopeng
	 * @date 09:42 2021/06/22
	 * @return java.lang.String
	 **/
	@GetMapping("/api/meaning/{code}")
	@ApiOperation("查询代码含义")
	@Cacheable(value = CR_CODE, key = "'MEANING_OF_' + ${symbol_pound}code")
	public JsonResult<String> meaning(@PathVariable String code) {
		CodeApiVo result = codeService.getCodeDetail(code);
		if(null != result) {
			return JsonResult.getInstant(ReturnCodeEnum.SUCCESS, result.getValue());
		}
		throw new BusinessException(ReturnCodeEnum.RESOURCE_NOT_FOUND);
	}

	/**
	 * Description:
	 * <获取上级代码详情>
	 * @param code 1
	 * @author wenxiaopeng
	 * @date 09:48 2021/06/22
	 * @return ${package}.common.bean.vo.CodeVo
	 **/
	@GetMapping("/api/upper/{code}")
	@ApiOperation("获取代码上级代码")
	@Cacheable(value = CR_CODE, key = "'UPPER_OF_' + ${symbol_pound}code")
	public JsonResult<CodeApiVo> father(@PathVariable String code) {
		CodeApiVo result = codeService.getCodeDetail(code);
		if(null!=result && null!=result.getUpperCodeId()) {
			CodeVo codeDetail = codeService.getCodeDetail(result.getUpperCodeId());
			return JsonResult.getInstant(ReturnCodeEnum.SUCCESS, codeDetail.asApiVo());
		}
		return null;
	}

	/**
	 * Description:
	 * <获取已有代码类型列表>
	 * @author wenxiaopeng
	 * @date 09:48 2021/06/22
	 * @return ${package}.common.bean.vo.CodeVo
	 **/
	@GetMapping("/api/types")
	@Cacheable(value = CR_CODE, key = "'CODE_TYPE_LIST'")
	public JsonResult<List<CodeTypeApiVo>> getCodeTypeList() {
		List<CodeType> poList = codeService.getCodeTypeList();
		return JsonResult.getInstant(ReturnCodeEnum.SUCCESS, poList.stream().map(CodeType::asApiVo).collect(Collectors.toList()));
	}
	/**
	 * Description:
	 * <新增代码>
	 *
	 * @param codeVo 1
	 * @return ${package}.common.bean.vo.JsonResult<${package}.corplution.bean.po.Code>
	 * @author wenxiaopeng
	 * @date
	 **/
	@ApiOperation(value = "新增代码")
	@PostMapping
	public JsonResult<Code> addCode(@RequestBody @Validated InsertCodeVo codeVo) {
		Code code = codeService.insertCode(codeVo);
		return JsonResult.getInstant(ReturnCodeEnum.SUCCESS, code);
	}

	/**
	 * Description:
	 * <新增代码类型>
	 * @param codeType 1
	 * @author wenxiaopeng
	 * @date 14:18 2021/06/17
	 * @return ${package}.common.bean.vo.JsonResult<${package}.common.bean.po.Code>
	 **/
	@ApiOperation(value = "新增代码类型")
	@PostMapping("/codetype")
	public JsonResult<CodeType> addCode(@RequestBody @Validated AddCodeTypeVo codeType) {
		CodeType type = codeService.addCodeType(codeType);
		return JsonResult.getInstant(ReturnCodeEnum.SUCCESS, type);
	}

	/**
	 * Description:
	 * <修改代码值>
	 *
	 * @param codeVo 1
	 * @return ${package}.common.bean.vo.JsonResult<${package}.corplution.bean.po.Code>
	 * @author wenxiaopeng
	 * @date
	 **/
	@ApiOperation(value = "修改代码值")
	@PutMapping
	public JsonResult<Code> updateCode(@RequestBody @Validated UpdateCodeVo codeVo) {
		Code code = codeService.updateCode(codeVo);
		return JsonResult.getInstant(ReturnCodeEnum.SUCCESS, code);
	}

	/**
	 * Description:
	 * <修改代码类型>
	 *
	 * @param codeVo 1
	 * @return ${package}.common.bean.vo.JsonResult<${package}.corplution.bean.po.Code>
	 * @author wenxiaopeng
	 * @date
	 **/
	@ApiOperation(value = "修改代码类型")
	@PutMapping("/codetype")
	public JsonResult<CodeType> updateCodeType(@RequestBody @Validated UpdateCodeTypeVo codeVo) {
		CodeType codeType = codeService.updateCodeType(codeVo);
		return JsonResult.getInstant(ReturnCodeEnum.SUCCESS, codeType);
	}

	/**
	 * Description:
	 * <修改代码状态>
	 *
	 * @param id   1
	 * @return ${package}.common.bean.vo.JsonResult
	 * @author wenxiaopeng
	 * @date
	 **/
	@ApiOperation(value = "修改代码状态")
	@PostMapping("/status")
	public JsonResult changeCodeStatus(@RequestParam Long id) {
		codeService.changeCodeStatus(id);
		return JsonResult.getInstant(ReturnCodeEnum.SUCCESS);
	}

	/**
	 * Description:
	 * <下载导入模板>
	 * @author wenxiaopeng
	 * @date 17:03 2021/06/16
	 **/
	@ApiOperation(value = "下载导入模板")
	@GetMapping("/template")
	public void downloadTemplate(HttpServletResponse response) {
		try(InputStream resourceAsStream = ResourceUtils.getResourceAsStream("template/code_import.xlsx")) {
			ResponseUtil.response(response, resourceAsStream, "代码导入模板.xlsx");
		} catch (IOException e) {
			// something goes wrong
		}
	}

	@GetMapping("/codetype")
	@ApiOperation("获取CodeType详情，包含代码树")
	public JsonResult<CodeTypeVo> getCodeTypeInfo(@RequestParam Long codeTypeId) {
		CodeTypeVo result = codeService.getCodeTypeDetail(codeTypeId);
		return JsonResult.getInstant(ReturnCodeEnum.SUCCESS, result);
	}

	/**
	 * Description:
	 * <导入多级代码>
	 *
	 * @param codeVo 1
	 * @return ${package}.common.bean.vo.JsonResult
	 * @author wenxiaopeng
	 * @date
	 **/
	@ApiOperation(value = "导入代码")
	@PostMapping("/import")
	public JsonResult importCode(@Validated UploadCodeVo codeVo) {

		CodeTypeVo result = codeService.handleImport(codeVo);
		return JsonResult.getInstant(ReturnCodeEnum.SUCCESS, result);
	}

	/**
	 * Description:
	 * <拖动代码值顺序>
	 * @author zhoutt
	 * @date 15:02 2020/10/27
	 * @param codeMoveVo 1
	 * @return ${package}.common.bean.vo.JsonResult
	 **/
	@PostMapping("/move")
	@ApiOperation(value = "拖动代码值顺序")
	public JsonResult moveCode(@Validated @RequestBody CodeMoveVo codeMoveVo) {
		codeService.moveCode(codeMoveVo);
		return JsonResult.getInstant(ReturnCodeEnum.SUCCESS);
	}

	/**
	 * Description:
	 * <生成代码文件 style取值 class/enum>
	 * @param style 1
	 * @author wenxiaopeng
	 * @date 16:21 2021/06/17
	 **/
	@GetMapping("/source/{style}")
	@ApiOperation(value = "下载java代码")
	public void getJavaCode(@RequestParam Long codeTypeId, @PathVariable SourceCodeUtil.CodeStyle style, HttpServletResponse response) {

		File sourceCodeFile = codeService.generateSourceCode(codeTypeId, style);
		try {
			ResponseUtil.response(response, new FileInputStream(sourceCodeFile), sourceCodeFile.getName());
		} catch (FileNotFoundException e) {
			log.error("error info ",e);
		} finally {
			sourceCodeFile.delete();
		}
	}

	/**
	 * Description:
	 * <>
	 * @param codeTypeId 1
	 * @author wenxiaopeng
	 * @date 12:04 2021/06/21
	 **/
	@GetMapping("/download/excel")
	@ApiOperation(value = "下载code Excel")
	public void downloadCode(@RequestParam Long codeTypeId, HttpServletResponse response) {

		XSSFWorkbook sheets = codeService.downloadCode(codeTypeId);
		try {
			ResponseUtil.setFileHeader(response, "代码详情.xlsx");
			sheets.write(response.getOutputStream());
		} catch (IOException e) {
			log.error("write excel error: {}", e.getMessage());
		}
	}

	@ApiOperation("刷新code缓存")
	@DeleteMapping("/cache")
	public JsonResult flushCodeCache() {
		synchronized (CodeController.class) {
			try {
				log.info("flushing cache...");
				Cache cache = cacheManager.getCache(CR_CODE);
				if(null != cache) {
					cache.clear();
				}
			} catch (Exception e) {
				throw new BusinessException(e.getMessage());
			}
		}
		return JsonResult.getInstant(ReturnCodeEnum.SUCCESS);
	}
}
